package com.xiaoq.coms.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

@SuppressWarnings("rawtypes")
@Component
public class JsonUtil {
	
	private static final String DF = "yyyy-MM-dd HH:mm:ss";

	private static ObjectMapper OM = new ObjectMapper();
	private static JsonFactory JF = new JsonFactory();
	static {
		OM.setDateFormat(new SimpleDateFormat(DF));
		OM.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
		OM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,	false);
		OM.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		OM.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
	}
	
	@SuppressWarnings("unused")
	private static class InnerUser {
		public String getName() {
			return name;
		}
		
		public void setName(String name) {
			this.name = name;
		}
		
		public Integer getAge() {
			return age;
		}
		
		public void setAge(Integer age) {
			this.age = age;
		}
		
		public void setUser(InnerCompany company) {
			this.company = company;
		}
		
		public InnerCompany getUser() {
			return this.company;
		}
		
		private String name;
		private Integer age;
		private InnerCompany company;
	}
	
	@SuppressWarnings("unused")
	private static class InnerCompany {
		public String getName() {
			return name;
		}
		
		public void setName(String name) {
			this.name = name;
		}
		
		public Integer getAge() {
			return age;
		}
		
		public void setAge(Integer age) {
			this.age = age;
		}
		
		private String name;
		private Integer age;
	}
	
	public static void main(String[] args) {
		
		String jStr = "[{\"name\" : \"hello\", \"age\" : 10, 'user' : {\"name\" : \"world\", \"age\" : 20}},{\"name\" : \"world\", \"age\" : 20, x : 10}]";
		List<InnerUser> list = JsonUtil.asList(jStr, InnerUser.class);
		System.out.println(list.get(0).getUser().getName());
		List<Object> list2 = JsonUtil.asList(jStr, Object.class);
		System.out.println(list2.get(0).getClass().getCanonicalName());
		
		jStr = "{\"name\" : \"world\", \"age\" : 20}";
		Map<String, Object> map = JsonUtil.asMapWithStringKey(jStr, Object.class);
		System.out.println(map.get("age"));
		OutputStream os = null;
		try {
			os = new ByteArrayOutputStream();
			JsonUtil.write2Stream(map, os);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	@SuppressWarnings("unchecked")
	public static Object asObject(String jStr,  Class cClazz,
			Class eClazz) {
		try {
			JavaType type = OM.getTypeFactory().
					  constructCollectionType(cClazz, eClazz);
			return OM.readValue(jStr, type);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public static Object asObject(InputStream ins,  Class cClazz,
			Class eClazz) {
		try {
			JavaType type = OM.getTypeFactory().
					  constructCollectionType(cClazz, eClazz);
			return OM.readValue(ins, type);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> List<T> asList(String jStr, Class<T> eClazz) {
		try {
			JavaType type = OM.getTypeFactory().
					  constructCollectionType(List.class, eClazz);
			return OM.readValue(jStr, type);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> List<T> asList(InputStream ins, Class<T> eClazz) {
		try {
			JavaType type = OM.getTypeFactory().
					  constructCollectionType(List.class, eClazz);
			return OM.readValue(ins, type);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> Map<String, T> asMapWithStringKey(String jStr, 
			Class<T> eClazz) {
		try {
			JavaType type = OM.getTypeFactory().constructMapType(HashMap.class, String.class, eClazz);
			return OM.readValue(jStr, type);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> Map<String, T> asMapWithStringKey(InputStream ins, 
			Class<T> eClazz) {
		try {
			JavaType type = OM.getTypeFactory().constructMapType(HashMap.class, String.class, eClazz);
			return OM.readValue(ins, type);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> T asObject(String jStr, Class<T> oClazz) {
		try {
			return OM.readValue(jStr, oClazz);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> T asObject(InputStream ins, Class<T> oClazz) {
		try {
			return OM.readValue(ins, oClazz);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static <T> T asObject(byte[] ins, Class<T> oClazz) {
		try {
			return OM.readValue(ins, oClazz);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	public static String asString(Object obj, boolean pp) {
		try{
			StringWriter sw = new StringWriter();
			JsonGenerator jg = JF.createGenerator(sw);
			if (pp) {
				jg.useDefaultPrettyPrinter();
			}
			OM.writeValue(jg, obj);
			return sw.toString();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	public static String asString(Object obj) {
		return asString(obj, false);
	}
	
	public static void write2Stream(Object obj, OutputStream os, boolean doFlush) {
		try{
			OM.writeValue(os, obj);
			if(doFlush) {
				os.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static void write2Stream(Object obj, OutputStream os) {
		write2Stream(obj, os, true);
	}
	
    
}